/********************************************************************************
  * @file    verilogsyntaxhighlighter.cpp
  * @author  Lun Li
  * @version V1.4.0
  * @date    2022.3.23
  * @brief   This file provides all the VerilogSyntaxHighlighter functions.
  *******************************************************************************/

#include "verilogsyntaxhighlighter.h"

VerilogSyntaxHighlighter::VerilogSyntaxHighlighter(ProgramConfig *const config, QTextDocument *parent)
    :QSyntaxHighlighter(parent),
      m_config(config)
{
    HighlightingRule rule;

    keywordFormat.setForeground(config->keywordColor());
    keywordFormat.setFontWeight(QFont::Bold);
    const QString keywordPatterns[] = {
        QStringLiteral("\\band\\b"), QStringLiteral("\\balways\\b"), QStringLiteral("\\bassign\\b"),
        QStringLiteral("\\bbegin\\b"), QStringLiteral("\\bbuf\\b"), QStringLiteral("\\bbufif0\\b"),
        QStringLiteral("\\bbufif1\\b"), QStringLiteral("\\bcase\\b"), QStringLiteral("\\bcasex\\b"),
        QStringLiteral("\\bcasez\\b"), QStringLiteral("\\bcmos\\b"), QStringLiteral("\\bdeassign\\b"),
        QStringLiteral("\\bdefault\\b"), QStringLiteral("\\bdefparam\\b"), QStringLiteral("\\bdisable\\b"),
        QStringLiteral("\\bedge\\b"), QStringLiteral("\\belse\\b"), QStringLiteral("\\bend\\b"),
        QStringLiteral("\\bendcase\\b"), QStringLiteral("\\bendfunction\\b"), QStringLiteral("\\bendprimitive\\b"),
        QStringLiteral("\\bendmodule\\b"), QStringLiteral("\\bendspecify\\b"), QStringLiteral("\\bendtable\\b"),
        QStringLiteral("\\bendtask\\b"), QStringLiteral("\\bevent\\b"), QStringLiteral("\\bfor\\b"),
        QStringLiteral("\\bforce\\b"), QStringLiteral("\\bforever\\b"), QStringLiteral("\\bfork\\b"),
        QStringLiteral("\\bfunction\\b"), QStringLiteral("\\bhighz0\\b"), QStringLiteral("\\bhighz1\\b"),
        QStringLiteral("\\bif\\b"), QStringLiteral("\\bifnone\\b"), QStringLiteral("\\binitial\\b"),
        QStringLiteral("\\binout\\b"), QStringLiteral("\\binput\\b"), QStringLiteral("\\binteger\\b"),
        QStringLiteral("\\bjoin\\b"), QStringLiteral("\\blarge\\b"), QStringLiteral("\\bmacromodule\\b"),
        QStringLiteral("\\bmedium\\b"), QStringLiteral("\\bmodule\\b"), QStringLiteral("\\bnand\\b"),
        QStringLiteral("\\bnegedge\\b"), QStringLiteral("\\bnor\\b"), QStringLiteral("\\bnot\\b"),
        QStringLiteral("\\bnotif0\\b"), QStringLiteral("\\bnotif1\\b"), QStringLiteral("\\bnmos\\b"),
        QStringLiteral("\\bor\\b"), QStringLiteral("\\boutput\\b"), QStringLiteral("\\bparameter\\b"),
        QStringLiteral("\\bpmos\\b"), QStringLiteral("\\bposedge\\b"), QStringLiteral("\\bprimitive\\b"),
        QStringLiteral("\\bpulldown\\b"), QStringLiteral("\\bpullup\\b"), QStringLiteral("\\bpull0\\b"),
        QStringLiteral("\\bpull1\\b"), QStringLiteral("\\brcmos\\b"), QStringLiteral("\\breal\\b"),
        QStringLiteral("\\brealtime\\b"), QStringLiteral("\\breg\\b"), QStringLiteral("\\brelease\\b"),
        QStringLiteral("\\brepeat\\b"), QStringLiteral("\\brnmos\\b"), QStringLiteral("\\brpmos\\b"),
        QStringLiteral("\\brtran\\b"), QStringLiteral("\\brtranif0\\b"), QStringLiteral("\\brtranif1\\b"),
        QStringLiteral("\\bscalared\\b"), QStringLiteral("\\bsmall\\b"), QStringLiteral("\\bspecify\\b"),
        QStringLiteral("\\bspecparam\\b"), QStringLiteral("\\bstrength\\b"), QStringLiteral("\\bstrong0\\b"),
        QStringLiteral("\\bstrong1\\b"), QStringLiteral("\\bsupply0\\b"), QStringLiteral("\\bsupply1\\b"),
        QStringLiteral("\\btable\\b"), QStringLiteral("\\btask\\b"), QStringLiteral("\\btran\\b"),
        QStringLiteral("\\btranif0\\b"), QStringLiteral("\\btranif1\\b"), QStringLiteral("\\btime\\b"),
        QStringLiteral("\\btri\\b"), QStringLiteral("\\btriand\\b"), QStringLiteral("\\btrior\\b"),
        QStringLiteral("\\btrireg\\b"), QStringLiteral("\\btri0\\b"), QStringLiteral("\\btri1\\b"),
        QStringLiteral("\\bvectored\\b"), QStringLiteral("\\bwait\\b"), QStringLiteral("\\bwand\\b"),
        QStringLiteral("\\bweak0\\b"), QStringLiteral("\\bweak1\\b"), QStringLiteral("\\bwhile\\b"),
        QStringLiteral("\\bwire\\b"), QStringLiteral("\\bwor\\b"), QStringLiteral("\\bxnor\\b"),
        QStringLiteral("\\bxor\\b")
    };
    for (const QString &pattern : keywordPatterns) {
        rule.pattern = QRegularExpression(pattern);
        rule.format = &keywordFormat;
        highlightingRules.append(rule);
    }

    quotationFormat.setForeground(config->quotationColor());
    rule.pattern = QRegularExpression(QStringLiteral("\".*\""));
    rule.format = &quotationFormat;
    highlightingRules.append(rule);

    numberFormat.setForeground(config->numberColor());
    const QString numberPatterns[] = {
        QStringLiteral("(?<![_a-zA-Z\\$])\\d*\\'[bo]?[0-9xzXZ_]*"),
        QStringLiteral("(?<![_a-zA-Z\\$])\\d*\\'d?[0-9_]*"),
        QStringLiteral("(?<![_a-zA-Z\\$])\\d*\\'h[0-9xzXZa-fA-F_]*"),
        QStringLiteral("(?<![_a-zA-Z\\$])\\b[0-9_]*\\b"),
        QStringLiteral("(?<![_a-zA-Z\\$])-?\\d*\\.\\d(?:[eE]-?\\d*)?\\b")
    };
    for (const QString &pattern : numberPatterns) {
        rule.pattern = QRegularExpression(pattern);
        rule.format = &numberFormat;
        highlightingRules.append(rule);
    }

    systemFunctionFormat.setForeground(config->systemFunctionColor());
    rule.pattern = QRegularExpression(QStringLiteral("(?<![_a-zA-Z\\$])\\$\\w+\\b"));
    rule.format = &systemFunctionFormat;
    highlightingRules.append(rule);

    singleLineCommentExpression = QRegularExpression(QStringLiteral("//"));
    commentStartExpression = QRegularExpression(QStringLiteral("/\\*"));
    commentEndExpression = QRegularExpression(QStringLiteral("\\*/"));

    commentFormat.setForeground(config->commentColor());
    connect(config, &ProgramConfig::editorConfigChanged, this, &VerilogSyntaxHighlighter::setColor);
}

void VerilogSyntaxHighlighter::highlightBlock(const QString &text)
{        
    for (const HighlightingRule &rule : qAsConst(highlightingRules)) {
        QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
        while (matchIterator.hasNext()) {
            QRegularExpressionMatch match = matchIterator.next();
            setFormat(match.capturedStart(), match.capturedLength(), *rule.format);
        }
    }
    setCurrentBlockState(0);
    bool currentLineHasComment = false;
    int startIndex = 0;
    if (previousBlockState() != 1) {
        startIndex = text.indexOf(commentStartExpression);
        currentLineHasComment = true;
    }
    if (startIndex < 0) {
        startIndex = text.indexOf(singleLineCommentExpression);
        if (startIndex >= 0) {
            setFormat(startIndex, text.length() - startIndex, commentFormat);
            return;
        }
    }
    while (startIndex >= 0) {
        QRegularExpressionMatch match = commentEndExpression.match(text, startIndex + (currentLineHasComment ? 2 : 0));
        int endIndex = match.capturedStart();
        int commentLength = 0;
        if (endIndex == -1) {
            setCurrentBlockState(1);
            commentLength = text.length() - startIndex;
        } else {
            commentLength = endIndex - startIndex
                            + match.capturedLength();
        }
        setFormat(startIndex, commentLength, commentFormat);
        int singleLineCommentIndex = text.indexOf(singleLineCommentExpression, endIndex + 2);
        startIndex = text.indexOf(commentStartExpression, startIndex + commentLength);
        if (startIndex > singleLineCommentIndex && singleLineCommentIndex >= 0) {
            setFormat(singleLineCommentIndex, text.length() - singleLineCommentIndex, commentFormat);
            break;
        }
    }
}

void VerilogSyntaxHighlighter::setColor()
{
    keywordFormat.setForeground(m_config->keywordColor());
    quotationFormat.setForeground(m_config->quotationColor());
    numberFormat.setForeground(m_config->numberColor());
    commentFormat.setForeground(m_config->commentColor());
    systemFunctionFormat.setForeground(m_config->systemFunctionColor());
    rehighlight();
}
